package com.video.netty;

import com.video.commons.runtimelog.RunLogUtil;
import com.video.netty.commons.utils.ChannelUtil;
import com.video.netty.commons.utils.NettySSLUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import java.net.URI;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Netty客户端启动 - 连接golang服务器
 */
@Component
public class NettyRunning {

    private EventLoopGroup loopGroup = new NioEventLoopGroup();

    @Value("${golang.heart}")
    private int heartSec;

    @Value("${golang.reconn}")
    private int reconnSec;

    @Value("${golang.readTimeout}")
    private int readTimeout;

    @Value("${server.ssl.enabled}")
    private boolean sslEnable;

    @Value("${golang.enable}")
    private boolean golangEnable;

    @Autowired
    private ChannelUtil channelUtil;

    @Autowired
    private NettySSLUtil sslUtil;

    @Autowired
    private List<SimpleChannelInboundHandler> inChannelHandlers;

    @Autowired
    private List<ChannelOutboundHandlerAdapter> outChannelHandler;

    /**
     * 连接golang服务器
     */
    @Async
    public void connection() throws Exception {

        //如果参数为false，则不连接golang服务器
        if(!golangEnable){
            RunLogUtil.info("已经关闭了golang服务器的连接,如果需要连接,请在配置参数中打开.....");
            return;
        }

        //websocket请求地址
        String golangUri = sslUtil.getWsPath();
        URI websocketURI = new URI(golangUri);
        HttpHeaders httpHeaders = new DefaultHttpHeaders();

//        System.out.println("--------->Netty入站的消息处理器：");
//        for (SimpleChannelInboundHandler inChannelHandler : inChannelHandlers) {
//            System.out.println(inChannelHandler.getClass().getName());
//        }
//        System.out.println("--------->Netty入站的消息处理器!!!!");

        Bootstrap bootstrap = new Bootstrap()
                .group(loopGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer() {
                    @Override
                    protected void initChannel(Channel channel) throws Exception {
                        ChannelPipeline pipeline = channel.pipeline();

                        //根据参数决定是否配置netty客户端的ssl
                        if(sslEnable){
                            //配置客户端ssl
                            SSLContext sslContext = sslUtil.createSSLContext(); ///SslUtil自定义类
                            SSLEngine sslEngine = sslContext.createSSLEngine();
                            sslEngine.setUseClientMode(true); /// 是否使用客户端模式
                            sslEngine.setNeedClientAuth(false); ////是否需要验证客户端
                            pipeline.addLast("ssl", new SslHandler(sslEngine));
                        }

                        pipeline.addLast(new HttpClientCodec());
                        pipeline.addLast(new HttpObjectAggregator(1024 * 1024 * 10));
                        //处理websocket升级握手
                        pipeline.addLast(new WebSocketClientProtocolHandler(WebSocketClientHandshakerFactory
                                .newHandshaker(websocketURI,
                                        WebSocketVersion.V13,
                                        null,
                                        true,
                                        httpHeaders)));

                        //心跳机制处理
                        pipeline.addLast(new IdleStateHandler(readTimeout, heartSec, 0, TimeUnit.SECONDS));

                        //自定义出站handler
                        pipeline.addLast(outChannelHandler.toArray(new ChannelOutboundHandlerAdapter[0]));

                        //自定义的入站handler
                        pipeline.addLast(inChannelHandlers.toArray(new SimpleChannelInboundHandler[0]));

                    }
                });

        try {
            ChannelFuture future = bootstrap.connect(websocketURI.getHost(), websocketURI.getPort()).sync();
            RunLogUtil.info("[connection success] Golang服务器连接成功，地址为：" + golangUri);

            //获得连接golang服务器的channel对象
            Channel channel = future.channel();
            channel.closeFuture().addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                    reconn();
                }
            });
            //缓存该channel对象
            channelUtil.setChannel(channel);
        } catch (Exception e) {
            RunLogUtil.error("[connection fail] Golang服务器连接失败！地址：" + golangUri, e);
            reconn();
        }
    }

    /**
     * 重连服务器
     */
    private void reconn() throws Exception {
        Thread.sleep(reconnSec * 1000);
        //进行重连
        RunLogUtil.warn("开始重连Golang服务器.......");
        connection();
    }

    /**
     * 关闭线程池资源
     */
    @PreDestroy
    private void destory(){
        loopGroup.shutdownGracefully();
    }
}
